import time
import collections
from concurrent.futures import ThreadPoolExecutor
import coin.exchange.upbit_v1.kr_rest.private_client as privc
import coin.exchange.upbit_v1.kr_rest.product as exchange_product
from coin.exchange.base.kr_rest.order_base import OrderBase


def voting(results):
  if len(results) == 1:
    # not doing voting
    return results[0]
  else:
    etag_count = collections.defaultdict(int)

    for result in results:
      etag_count[result.native_header['etag']] += 1

    if len(etag_count) == 1:
      return results[0]
    if len(results) == 2:
      print(etag_count)
      return None
    else:
      if len(etag_count) == 2:
        keys = [key for key, value in etag_count.items() if value != 1]
        assert len(keys) == 1, keys
        for result in results:
          if result.native_header['etag'] == keys[0]:
            return result
      else:
        return None
  raise NotImplementedError("Cannot reach here")


class UpbitV1Order(OrderBase):
  ProductType = exchange_product.UpbitProduct

  def __init__(self, products, key_file, num_redundancy=4):
    OrderBase.__init__(self, products, key_file)
    assert num_redundancy >= 0, num_redundancy
    self.clients = []
    for i in range(1 + num_redundancy):
      self.clients.append(privc.UpbitPrivateClient(key_file))
    self.client = self.clients[0]

  def query_balance_impl(self):
    balances = []
    with ThreadPoolExecutor(max_workers=len(self.clients)) as executor:
      result_list = [executor.submit(client.query_account_balance) for client in self.clients]
      time.sleep(0.2)
      balances = [x.result(timeout=1) for x in result_list]
    balance = voting(balances)
    return balance

  def query_list_orders(self, product):
    return self.client.query_list_orders(product)

  # TODO(jshin): check whether |product |is the same as one of products provided
  # in constructor
  def query_submit(self, product, side, price, qty):
    self.last_order = self.client.query_submit(product, side, price, qty)
    return self.last_order

  def query_cancel(self, product, order_id):
    return self.client.query_cancel(product, order_id)

  def query_cancel_product(self, product):
    return self.client.query_cancel_product(product)
